package com.jxpanda.spring.module.wechat.toolkit;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.jxpanda.spring.module.wechat.component.message.pojo.MsgType;
import com.jxpanda.spring.module.wechat.component.message.pojo.WechatXmlMessage;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * CorpEncryptKit提供企业微信加解密工具方法。
 *
 * @author Panda
 */
@Slf4j
public class CorpMessageKit {

    /**
     * 判断消息类型的正则表达式
     */
    private static final Pattern MSG_TYPE_PATTERN = Pattern.compile("<MsgType><!\\[CDATA\\[(.*?)]]></MsgType>");

    /**
     * 用于生成随机字符串的字符集
     */
    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    private static final int PKCS7_PAD_LENGTH = 32;

    public static <T extends WechatXmlMessage> T parseXmlMessage(String xml) {
        MsgType msgType = getMsgType(xml);
        if (msgType == MsgType.UNKNOWN) {
            return null;
        }
        XmlMapper xmlMapper = new XmlMapper();
        try {
            return xmlMapper.readValue(xml, new TypeReference<>() {
                @Override
                public Type getType() {
                    return msgType.getXmlClass();
                }
            });
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static MsgType getMsgType(String xml) {
        Matcher matcher = MSG_TYPE_PATTERN.matcher(xml);
        if (matcher.find()) {
            return MsgType.fromValue(matcher.group(1));
        }
        return MsgType.UNKNOWN;
    }

    /**
     * 生成4个字节的网络字节序
     *
     * @param sourceNumber 源数字
     * @return 转换后的网络字节序字节数组
     */
    public static byte[] getNetworkBytesOrder(int sourceNumber) {
        byte[] orderBytes = new byte[4];
        orderBytes[3] = (byte) (sourceNumber & 0xFF);
        orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);
        orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);
        orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);
        return orderBytes;
    }

    /**
     * 还原4个字节的网络字节序
     *
     * @param orderBytes 网络字节序字节数组
     * @return 还原后的数字
     */
    public static int recoverNetworkBytesOrder(byte[] orderBytes) {
        int sourceNumber = 0;
        for (int i = 0; i < 4; i++) {
            sourceNumber <<= 8;
            sourceNumber |= orderBytes[i] & 0xff;
        }
        return sourceNumber;
    }


    /**
     * 生成一个随机字符串
     *
     * @return 随机字符串
     */
    public static String generateRandomString() {
        Random random = new Random();
        return IntStream.range(0, 16)
                .mapToObj(i -> CHARACTERS.charAt(random.nextInt(CHARACTERS.length())))
                .map(String::valueOf)
                .collect(Collectors.joining());
    }


    /**
     * 获得对明文进行补位填充的字节。
     *
     * @param count 需要进行填充补位操作的明文字节个数
     * @return 补齐用的字节数组
     */
    public static byte[] pkcs7PaddingBytes(int count) {
        // 计算需要填充的位数
        int amountToPad = PKCS7_PAD_LENGTH - (count % PKCS7_PAD_LENGTH);
        // 获得补位所用的字符
        byte charByte = (byte) (amountToPad & 0xFF);
        char padChar = (char) charByte;
        return String.valueOf(padChar).repeat(amountToPad).getBytes(StandardCharsets.UTF_8);
    }

    /**
     * 删除解密后明文的补位字符
     *
     * @param decrypted 解密后的明文
     * @return 删除补位字符后的明文
     */
    public static byte[] pkcs7Decode(byte[] decrypted) {
        int pad = decrypted[decrypted.length - 1];
        if (pad < 1 || pad > PKCS7_PAD_LENGTH) {
            pad = 0;
        }
        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
    }

}
